/**
 * Utilities.java
 * Nov 25th 2009
 */
package tcss543.cpap.common;

import java.math.BigDecimal;
import java.util.Map;
import java.util.Set;

/**
 * A set of static functions that provide common utilities.
 * 
 * @author ashish
 * 
 */
public class Utilities {

	/**
	 * No need for an instance
	 */
	private Utilities() {

	}

	/**
	 * Returns the quality of the given solution for the given set of papers and
	 * reviewers.
	 * 
	 * @param solution
	 *            : The solution is a map of every reviewer id to the set of
	 *            paper id assigned.
	 * @param papers
	 *            : Papers mapped to their keywords
	 * @param reviewers
	 *            : Reviewers mapped to a map of their keyword rankings.
	 * @return
	 */
	public static BigDecimal calculateQuality(
			final Map<Short, Set<Short>> solution,
			final Map<Short, Set<Short>> papers,
			final Map<Short, Map<Short, ReviewerRank>> reviewers,
			final boolean linearQuality) {
		BigDecimal result = BigDecimal.ZERO;

		// for every reviewer in our solution
		for (short r : solution.keySet()) {
			// penalty is square of the difference between optimal papers and
			// actual assigned.
			int penalty = 0;
			if (linearQuality) {
				penalty = (int) Math
						.abs((Constants.OPTIMAL_PAPERS_PER_REVIEWER - solution
								.get(r).size()));

			} else {
				penalty = (int) Math.pow(
						(Constants.OPTIMAL_PAPERS_PER_REVIEWER - solution
								.get(r).size()), 2);
			}

			// cost of assigning the papers
			int assignmentCost = 0;

			// we look at each paper assigned to this reviewer
			for (short p : solution.get(r)) {
				boolean aYes, aMaybe, aNo;
				aYes = aMaybe = aNo = false;

				// look at the keywords assigned to paper p
				for (short k : papers.get(p)) {
					if (reviewers.get(r).get(k) == ReviewerRank.YES) {
						aYes = true;
					} else if (reviewers.get(r).get(k) == ReviewerRank.MAYBE) {
						aMaybe = true;
					} else if (reviewers.get(r).get(k) == ReviewerRank.NO) {
						aNo = true;
					} else
						throw new RuntimeException(
								"Someone added a new rank but did not tell me what to do.");

					// if we get one yes we are done
					if (aYes)
						break;
				}

				// add the cost of this paper's assignment to the reviewers cost
				if (aYes) {
					assignmentCost += Constants.ASSIGNMENT_COST;
				} else if (aMaybe == false && aNo) {
					assignmentCost -= Constants.ASSIGNMENT_COST;
				}
			}

			// add the cost of this reviewer to total cost
			result = result.add(new BigDecimal(assignmentCost));
			// reduce by penalty charged
			result = result.subtract(new BigDecimal(penalty));
		}

		return result;
	}

	/**
	 * Validates that the solution has at least minimum number of reviewers.
	 * 
	 * @param encoding
	 * @return
	 */
	public static boolean atLeastMinimumReviewersPerPaper(
			final Map<Short, Set<Short>> encoding,
			final Map<Short, Set<Short>> papers) {
		boolean result = true;

		for (short p : papers.keySet()) {
			if (encoding.get(p).size() < Constants.MIN_NUM_OF_REVIEWERS_PER_PAPER) {
				result = false;
				break;
			}
		}

		return result;
	}

}
